Using the OS GeoDataViz Toolkit to assist with styling of location
data insights.
Qualitative Styling
When styling qualitative (categorical) data we need to be mindful of
the number of distinct classes. The GDV qualitative colours supports up
to eight classes. Beyond approximately 10 classes it becomes very
difficult to design a palette that preserves the separation between
colours allowing clear class identification.
library(sf)
library(tmap)
library(httr)
library(jsonlite)
Create sf data frame from GeoPackage (GPKG)
Combine the file reading and spatial subsetting by bounding box.
# Creating bounding box
bbox <- st_bbox(c(xmin = 503568.1996,
xmax = 561957.4962,
ymin = 155850.7975,
ymax = 200933.9026),
crs = st_crs(27700))
# Convert into a 'geometry'
bbox <- st_as_sfc(bbox)
# Create an sf data.frame object from the Greenspace file
# Can do subset using bounding box at same time as read in
osogs <- st_read('../../data/ordnance-survey/os-open-greenspace-gb.gpkg',
layer = 'greenspace_site',
wkt_filter = st_as_text(bbox)) # Subset the data to Greater London
Reading layer `greenspace_site' from data source
`/cloud/project/data/ordnance-survey/os-open-greenspace-gb.gpkg' using driver `GPKG'
Simple feature collection with 12529 features and 6 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: 502292 ymin: 155102 xmax: 562996.9 ymax: 207036.4
Projected CRS: OSGB 1936 / British National Grid
Subset data frame by function
Reduce the number of function categories to support qualitative
styling colour palette integration.
# Subset data frame by function, excluding 'Play Space' and 'Tennis Court'
osogs_filtered <- osogs[!osogs$function. %in% c('Play Space', 'Tennis Court'), ]
# List unique function values
unique(osogs_filtered$function.)
[1] "Allotments Or Community Growing Spaces" "Public Park Or Garden"
[3] "Religious Grounds" "Playing Field"
[5] "Bowling Green" "Cemetery"
[7] "Other Sports Facility" "Golf Course"
# Count number of unique function values
length(unique(osogs_filtered$function.))
[1] 8
OS Maps API ZXY resource
# OS Maps API layer
# Example uses Light Style in Web Mercator (EPSG:3857) projection
layer <- 'Light_3857'
# OS Data Hub project API key
key <- '7UTXMMWsGjjIzcBmLdAGnMO6WEAQi9Ng'
# Define the tile server parameters
url <- paste0('https://api.os.uk/maps/raster/v1/zxy/', layer,
'/{z}/{x}/{y}.png?key=', key)
OS GeoDataViz colour palettes
The OS GeoDataViz
toolkit provides qualitative, sequential, and diverging colour
palettes to support GDV applications.
# GDV colour palettes JSON file
gdv <- 'https://raw.githubusercontent.com/OrdnanceSurvey/GeoDataViz-Toolkit/master/Colours/GDV-colour-palettes-v0.7.json'
# Make HTTP GET request and decode JSON
gdv_json <- GET(url = gdv)
gdv_json <- fromJSON(content(gdv_json))
# Get colour hex values
gdv_qual <- unlist(gdv_json$qualitative$lookup)
# Show an example of the colours
(image(1:length(gdv_qual), 1, as.matrix(1:length(gdv_qual)),
col = gdv_qual,
xlab="", ylab = "", xaxt = "n", yaxt = "n", bty = "n"))
NULL

Plot data frame ontop of interactive map
Employ categorical styling via OS GeoDataViz (GDV) qualitative colour
palette.
# Generate a basic plot of the features
p <- tm_shape(osogs_filtered) +
tm_polygons('function.',
pal = gdv_qual,
popup.vars = FALSE)
# Change plotting mode from static to interactive
tmap_mode('view')
tmap mode set to interactive viewing
# Combine the features and base map
final_plot <- p +
tm_basemap(server = url)
final_plot
Warning: palette colors names missing for Allotments Or Community Growing Spaces, Bowling Green, Cemetery, Golf Course, Other Sports Facility, Playing Field, Public Park Or Garden, Religious Grounds. Therefore, palette color names will be ignored
# Change back to the static plotting mode
tmap_mode('plot')
tmap mode set to plotting
LS0tCnRpdGxlOiAiMDEgT1MgR2VvRGF0YVZpeiBUb29sa2l0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpVc2luZyB0aGUgT1MgR2VvRGF0YVZpeiBUb29sa2l0IHRvIGFzc2lzdCB3aXRoIHN0eWxpbmcgb2YgbG9jYXRpb24gZGF0YQppbnNpZ2h0cy4KCiMjIE9TIEdlb0RhdGFWaXogVG9vbGtpdAoKVGhlIE9TIFtHZW9EYXRhVml6IChHRFYpClRvb2xraXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9PcmRuYW5jZVN1cnZleS9HZW9EYXRhVml6LVRvb2xraXQpIHByb3ZpZGVzIGEgc2V0IG9mCnJlc291cmNlcyB0byBoZWxwIHlvdSBjb21tdW5pY2F0ZSB5b3VyIGRhdGEgZWZmZWN0aXZlbHkgdGhyb3VnaCB0aGUgZGVzaWduIG9mCmNvbXBlbGxpbmcgdmlzdWFscy4KClRoZSB0b29sa2l0IHByb3ZpZGVzIGEgc2VyaWVzIG9mIGNvbG91ciBwYWxldHRlcyB3aGljaCBwcm92aWRlIGEgZ3JlYXQgc3RhcnRpbmcKcG9pbnQgZm9yIHRoZW1hdGljIG1hcCBkZXNpZ24uIFRoZSBwYWxldHRlcyBjYW4gYmUgcHJvZ3JhbW1hdGljYWxseSBhY2Nlc3NlZCB2aWEKSlNPTiBjb3B5IGluIHRoZSByZXBvLgoKIyMgUXVhbGl0YXRpdmUgU3R5bGluZwoKV2hlbiBzdHlsaW5nIHF1YWxpdGF0aXZlIChjYXRlZ29yaWNhbCkgZGF0YSB3ZSBuZWVkIHRvIGJlIG1pbmRmdWwgb2YgdGhlIG51bWJlcgpvZiBkaXN0aW5jdCBjbGFzc2VzLiBUaGUgR0RWIHF1YWxpdGF0aXZlIGNvbG91cnMgc3VwcG9ydHMgdXAgdG8gZWlnaHQgY2xhc3Nlcy4KQmV5b25kIGFwcHJveGltYXRlbHkgMTAgY2xhc3NlcyBpdCBiZWNvbWVzIHZlcnkgZGlmZmljdWx0IHRvIGRlc2lnbiBhIHBhbGV0dGUKdGhhdCBwcmVzZXJ2ZXMgdGhlIHNlcGFyYXRpb24gYmV0d2VlbiBjb2xvdXJzIGFsbG93aW5nIGNsZWFyIGNsYXNzCmlkZW50aWZpY2F0aW9uLgoKLS0tCgpgYGB7cn0KbGlicmFyeShzZikKbGlicmFyeSh0bWFwKQpsaWJyYXJ5KGh0dHIpCmxpYnJhcnkoanNvbmxpdGUpCmBgYAoKIyMgQ3JlYXRlIGBzZmAgZGF0YSBmcmFtZSBmcm9tIEdlb1BhY2thZ2UgKEdQS0cpCgpDb21iaW5lIHRoZSBmaWxlIHJlYWRpbmcgYW5kIHNwYXRpYWwgc3Vic2V0dGluZyBieSBib3VuZGluZyBib3guCgpgYGB7cn0KIyBDcmVhdGluZyBib3VuZGluZyBib3gKYmJveCA8LSBzdF9iYm94KGMoeG1pbiA9IDUwMzU2OC4xOTk2LCAKICAgICAgICAgICAgICAgICAgeG1heCA9IDU2MTk1Ny40OTYyLCAKICAgICAgICAgICAgICAgICAgeW1pbiA9IDE1NTg1MC43OTc1LCAKICAgICAgICAgICAgICAgICAgeW1heCA9IDIwMDkzMy45MDI2KSwgCiAgICAgICAgICAgICAgICBjcnMgPSBzdF9jcnMoMjc3MDApKQoKIyBDb252ZXJ0IGludG8gYSAnZ2VvbWV0cnknCmJib3ggPC0gc3RfYXNfc2ZjKGJib3gpCgojIENyZWF0ZSBhbiBzZiBkYXRhLmZyYW1lIG9iamVjdCBmcm9tIHRoZSBHcmVlbnNwYWNlIGZpbGUKIyBDYW4gZG8gc3Vic2V0IHVzaW5nIGJvdW5kaW5nIGJveCBhdCBzYW1lIHRpbWUgYXMgcmVhZCBpbgpvc29ncyA8LSBzdF9yZWFkKCcuLi8uLi9kYXRhL29yZG5hbmNlLXN1cnZleS9vcy1vcGVuLWdyZWVuc3BhY2UtZ2IuZ3BrZycsCiAgICAgICAgICAgICAgICAgbGF5ZXIgPSAnZ3JlZW5zcGFjZV9zaXRlJywKICAgICAgICAgICAgICAgICB3a3RfZmlsdGVyID0gc3RfYXNfdGV4dChiYm94KSkgICMgU3Vic2V0IHRoZSBkYXRhIHRvIEdyZWF0ZXIgTG9uZG9uCmBgYAoKIyMgU3Vic2V0IGRhdGEgZnJhbWUgYnkgZnVuY3Rpb24KClJlZHVjZSB0aGUgbnVtYmVyIG9mIGZ1bmN0aW9uIGNhdGVnb3JpZXMgdG8gc3VwcG9ydCBxdWFsaXRhdGl2ZSBzdHlsaW5nIGNvbG91cgpwYWxldHRlIGludGVncmF0aW9uLgoKYGBge3J9CiMgU3Vic2V0IGRhdGEgZnJhbWUgYnkgZnVuY3Rpb24sIGV4Y2x1ZGluZyAnUGxheSBTcGFjZScgYW5kICdUZW5uaXMgQ291cnQnCm9zb2dzX2ZpbHRlcmVkIDwtIG9zb2dzWyFvc29ncyRmdW5jdGlvbi4gJWluJSBjKCdQbGF5IFNwYWNlJywgJ1Rlbm5pcyBDb3VydCcpLCBdCmBgYAoKYGBge3J9CiMgTGlzdCB1bmlxdWUgZnVuY3Rpb24gdmFsdWVzCnVuaXF1ZShvc29nc19maWx0ZXJlZCRmdW5jdGlvbi4pCmBgYAoKYGBge3J9CiMgQ291bnQgbnVtYmVyIG9mIHVuaXF1ZSBmdW5jdGlvbiB2YWx1ZXMKbGVuZ3RoKHVuaXF1ZShvc29nc19maWx0ZXJlZCRmdW5jdGlvbi4pKQpgYGAKCiMjIE9TIE1hcHMgQVBJIFpYWSByZXNvdXJjZQoKYGBge3J9CiMgT1MgTWFwcyBBUEkgbGF5ZXIKIyBFeGFtcGxlIHVzZXMgTGlnaHQgU3R5bGUgaW4gV2ViIE1lcmNhdG9yIChFUFNHOjM4NTcpIHByb2plY3Rpb24KbGF5ZXIgPC0gJ0xpZ2h0XzM4NTcnCgojIE9TIERhdGEgSHViIHByb2plY3QgQVBJIGtleQprZXkgPC0gJzdVVFhNTVdzR2pqSXpjQm1MZEFHbk1PNldFQVFpOU5nJwpgYGAKCmBgYHtyfQojIERlZmluZSB0aGUgdGlsZSBzZXJ2ZXIgcGFyYW1ldGVycwp1cmwgPC0gcGFzdGUwKCdodHRwczovL2FwaS5vcy51ay9tYXBzL3Jhc3Rlci92MS96eHkvJywgbGF5ZXIsCiAgICAgICAgICAgICAgJy97en0ve3h9L3t5fS5wbmc/a2V5PScsIGtleSkKYGBgCgojIyBPUyBHZW9EYXRhVml6IGNvbG91ciBwYWxldHRlcwoKVGhlIE9TIFtHZW9EYXRhVml6CnRvb2xraXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9PcmRuYW5jZVN1cnZleS9HZW9EYXRhVml6LVRvb2xraXQvdHJlZS9tYXN0ZXIvQ29sb3VycykKcHJvdmlkZXMgcXVhbGl0YXRpdmUsIHNlcXVlbnRpYWwsIGFuZCBkaXZlcmdpbmcgY29sb3VyIHBhbGV0dGVzIHRvIHN1cHBvcnQgR0RWCmFwcGxpY2F0aW9ucy4KCmBgYHtyfQojIEdEViBjb2xvdXIgcGFsZXR0ZXMgSlNPTiBmaWxlCmdkdiA8LSAnaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL09yZG5hbmNlU3VydmV5L0dlb0RhdGFWaXotVG9vbGtpdC9tYXN0ZXIvQ29sb3Vycy9HRFYtY29sb3VyLXBhbGV0dGVzLXYwLjcuanNvbicKCiMgTWFrZSBIVFRQIEdFVCByZXF1ZXN0IGFuZCBkZWNvZGUgSlNPTgpnZHZfanNvbiA8LSBHRVQodXJsID0gZ2R2KQpnZHZfanNvbiA8LSBmcm9tSlNPTihjb250ZW50KGdkdl9qc29uKSkKCiMgR2V0IGNvbG91ciBoZXggdmFsdWVzCmdkdl9xdWFsIDwtIHVubGlzdChnZHZfanNvbiRxdWFsaXRhdGl2ZSRsb29rdXApCgojIFNob3cgYW4gZXhhbXBsZSBvZiB0aGUgY29sb3VycwooaW1hZ2UoMTpsZW5ndGgoZ2R2X3F1YWwpLCAxLCBhcy5tYXRyaXgoMTpsZW5ndGgoZ2R2X3F1YWwpKSwgCiAgICAgIGNvbCA9IGdkdl9xdWFsLAogICAgICB4bGFiPSIiLCB5bGFiID0gIiIsIHhheHQgPSAibiIsIHlheHQgPSAibiIsIGJ0eSA9ICJuIikpCmBgYAoKIyMgUGxvdCBkYXRhIGZyYW1lIG9udG9wIG9mIGludGVyYWN0aXZlIG1hcAoKRW1wbG95IGNhdGVnb3JpY2FsIHN0eWxpbmcgdmlhIE9TIEdlb0RhdGFWaXogKEdEVikgcXVhbGl0YXRpdmUgY29sb3VyIHBhbGV0dGUuCgpgYGB7cn0KIyBHZW5lcmF0ZSBhIGJhc2ljIHBsb3Qgb2YgdGhlIGZlYXR1cmVzCnAgPC0gdG1fc2hhcGUob3NvZ3NfZmlsdGVyZWQpICsKICAgICAgdG1fcG9seWdvbnMoJ2Z1bmN0aW9uLicsIAogICAgICAgICAgICAgICAgICBwYWwgPSBnZHZfcXVhbCwKICAgICAgICAgICAgICAgICAgcG9wdXAudmFycyA9IEZBTFNFKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEwfQojIENoYW5nZSBwbG90dGluZyBtb2RlIGZyb20gc3RhdGljIHRvIGludGVyYWN0aXZlCnRtYXBfbW9kZSgndmlldycpCgojIENvbWJpbmUgdGhlIGZlYXR1cmVzIGFuZCBiYXNlIG1hcApmaW5hbF9wbG90IDwtIHAgKwogICAgICAgICAgICAgICAgdG1fYmFzZW1hcChzZXJ2ZXIgPSB1cmwpCgpmaW5hbF9wbG90CmBgYAoKYGBge3J9CiMgQ2hhbmdlIGJhY2sgdG8gdGhlIHN0YXRpYyBwbG90dGluZyBtb2RlCnRtYXBfbW9kZSgncGxvdCcpCmBgYAo=